(defun* first (c) (car c))
(defun* second (c) (car (cdr c)))
(defun* third (c) (car (cdr (cdr c))))

(defmacro* defun (name args &rest body)
  `(defun* ,name ,args
     (block ,name
	,@body)))

(defun integer-string (n)
  (flet ((a (b i n)
	    (if (eq n 0) 0
	      (let ((c (a b i (/ n 10))))
		(replace* b `(,(code-char (+ (char-code #\0) (% n 10)))) c 0 1)
		(+ 1 c)))))
	(if (integerp n)
	    (let ((b (make-buffer 8)))
	      (a b 0 n)
	      b))))

(defun concatenate (stg1 stg2)
  (let ((stg3 (make-buffer (+ (length stg1) (length stg2) 1))))
    (replace* stg3 stg1 0 0 (length stg1))
    (replace* stg3 stg2 (length stg1) 0 (length stg2))
    stg3))

(setq *gensym-counter* 0)
(defun gensym ()
  (setq *gensym-counter* (+ 1 *gensym-counter*))
  (make-symbol (concatenate "#:G" (integer-string (- *gensym-counter* 1)))))

(defmacro* dotimes (spec &rest body)
  (let ((var (first spec))
	(max (second spec))
	(a (gensym))
	(max* (gensym)))
    `(block ,a
	    (let ((,var 0) (,max* ,max))
	      (while*
	       (if (not (< ,var ,max*))
		   (return-from ,a t))
	       ,@body
	       (setq ,var (+ 1 ,var)))))))

(defmacro* dolist (spec &rest body)
  (let ((var (first spec))
	(lst (second spec))
	(l (gensym)))
    `(let ((,l ,lst))
       (block a
	      (while*
	       (if (not ,l) (return-from a t))
	       (let ((,var (car ,l)))
		 ,@body)
	       (setq ,l (cdr ,l)))))))

(setq newline (code-char 10))

(defun* write-line (str &optional (s nil))
  (dotimes (i (length str))
    (if s (write-char (elt str i) s)
      (write-char (elt str i))))
  (if s (write-char newline s)
    (write-char newline))
  t)

(defmacro* with-open-file (spec &rest body)
  `(let ((,(car spec) (open* ,(second spec))))
     ,@body
     (close* ,(car spec))))

(defmacro* == (&rest args)
  `(eq ,@args))

(defun = (&rest rest)
  (apply #'eq rest))

(defun fibeq (n)
  (if (eq n 0) 0
    (if (eq n 1) 1
      (+ (fibeq (- n 1)) (fibeq (- n 2))))))

(defun fibapply (n)
  (if (= n 0) 0
    (if (= n 1) 1
      (+ (fibapply (- n 1)) (fibapply (- n 2))))))

(defun fibmacro (n)
  (if (== n 0) 0
    (if (== n 1) 1
      (+ (fibmacro (- n 1)) (fibmacro (- n 2))))))

(defmacro* and (a &rest rest)
  (if rest
      `(if ,a (and ,@rest) nil)
    `,a))

(defmacro* multiple-value-list (form)
  `(multiple-value-call #'list ,form))

(defun mapcarcar (ls)
  (if (consp ls)
      (if (consp (car ls))
	  (let* ((tmp (multiple-value-list (mapcarcar (cdr ls))))
		 (a (first tmp)) (b (second tmp)))
	    (if (eq 1 (length tmp)) (return-from mapcarcar nil))
	    (values (cons (car (car ls)) a)
		    (cons (cdr (car ls)) b)))
	nil)
    (values nil nil)))

(defun mapcar (fn ls &rest rest)
  (let ((tmp (multiple-value-list (mapcarcar rest))))
    (if (eq 1 (length tmp))
	(return-from mapcar nil))
    (if (consp ls)
	(cons (apply fn (car ls) (car tmp))
	      (apply #'mapcar fn (cdr ls) (second tmp)))
      nil)))
